require.config({
	//baseUrl: base_url + 'static/plugins/ace/',
	paths : {
		"ace" : base_url + 'static/plugins/ace/',
		"source-code-pro" : base_url + 'static/plugins/ace-plugin/source-code-pro'
	},
	waitSeconds : 30,
	urlArgs: "bust=" + (new Date()).getTime()
});

define(function(require, exports, module) {
	"use strict";

	//require("source-code-pro");
	require("ace/lib/fixoldbrowsers");

	require("ace/multi_select");
	require("ace/ext/spellcheck");
	require("inline_editor");
	require("dev_util");
	require("file_drop");

	var config = require("ace/config");
	config.init();
	var env = {};

	var dom = require("ace/lib/dom");
	var net = require("ace/lib/net");
	var lang = require("ace/lib/lang");
	var useragent = require("ace/lib/useragent");

	var event = require("ace/lib/event");
	var theme = require("ace/theme/textmate");
	var EditSession = require("ace/edit_session").EditSession;
	var UndoManager = require("ace/undomanager").UndoManager;

	var HashHandler = require("ace/keyboard/hash_handler").HashHandler;

	var Renderer = require("ace/virtual_renderer").VirtualRenderer;
	var Editor = require("ace/editor").Editor;

	var whitespace = require("ace/ext/whitespace");

	var doclist = require("doclist");
	var modelist = require("ace/ext/modelist");
	var themelist = require("ace/ext/themelist");
	var layout = require("layout");
	var TokenTooltip = require("token_tooltip").TokenTooltip;
	var util = require("util");
	var saveOption = util.saveOption;
	var fillDropdown = util.fillDropdown;
	var bindCheckbox = util.bindCheckbox;
	var bindDropdown = util.bindDropdown;

	var ElasticTabstopsLite = require("ace/ext/elastic_tabstops_lite").ElasticTabstopsLite;

	var IncrementalSearch = require("ace/incremental_search").IncrementalSearch;

	var workerModule = require("ace/worker/worker_client");
	if (location.href.indexOf("noworker") !== -1) {
		workerModule.WorkerClient = workerModule.UIWorkerClient;
	}

	/*********** create editor ***************************/
	var container = document.getElementById("editor-container");

	// Splitting.
	var Split = require("ace/split").Split;
	var split = new Split(container, theme, 1);
	env.editor = split.getEditor(0);
	split.on("focus", function(editor) {
		env.editor = editor;
		updateUIEditorOptions();
	});
	env.split = split;
	window.env = env;

	var consoleEl = dom.createElement("div");
	container.parentNode.appendChild(consoleEl);
	consoleEl.style.cssText = "position:fixed; bottom:1px; right:0;\
border:1px solid #baf; z-index:100";

	var cmdLine = new layout.singleLineEditor(consoleEl);
	cmdLine.editor = env.editor;
	env.editor.cmdLine = cmdLine;

	env.editor.showCommandLine = function(val) {
		this.cmdLine.focus();
		if (typeof val == "string")
			this.cmdLine.setValue(val, 1);
	};

	/**
	 * This demonstrates how you can define commands and bind shortcuts to them.
	 */
	env.editor.commands.addCommands([ {
		name : "gotoline",
		bindKey : {
			win : "Ctrl-L",
			mac : "Command-L"
		},
		exec : function(editor, line) {
			if (typeof line == "object") {
				var arg = this.name + " " + editor.getCursorPosition().row;
				editor.cmdLine.setValue(arg, 1);
				editor.cmdLine.focus();
				return;
			}
			line = parseInt(line, 10);
			if (!isNaN(line))
				editor.gotoLine(line);
		},
		readOnly : true
	}, {
		name : "snippet",
		bindKey : {
			win : "Alt-C",
			mac : "Command-Alt-C"
		},
		exec : function(editor, needle) {
			if (typeof needle == "object") {
				editor.cmdLine.setValue("snippet ", 1);
				editor.cmdLine.focus();
				return;
			}
			var s = snippetManager.getSnippetByName(needle, editor);
			if (s)
				snippetManager.insertSnippet(editor, s.content);
		},
		readOnly : true
	}, {
		name : "focusCommandLine",
		bindKey : "shift-esc|ctrl-`",
		exec : function(editor, needle) {
			editor.cmdLine.focus();
		},
		readOnly : true
	}, {
		name : "nextFile",
		bindKey : "Ctrl-tab",
		exec : function(editor) {
			doclist.cycleOpen(editor, 1);
		},
		readOnly : true
	}, {
		name : "previousFile",
		bindKey : "Ctrl-shift-tab",
		exec : function(editor) {
			doclist.cycleOpen(editor, -1);
		},
		readOnly : true
	}, {
		name : "execute",
		bindKey : "ctrl+enter",
		exec : function(editor) {
			try {
				var r = window.eval(editor.getCopyText() || editor.getValue());
			} catch (e) {
				r = e;
			}
			editor.cmdLine.setValue(r + "");
		},
		readOnly : true
	}, {
		name : "showKeyboardShortcuts",
		bindKey : {
			win : "Ctrl-Alt-h",
			mac : "Command-Alt-h"
		},
		exec : function(editor) {
			config.loadModule("ace/ext/keybinding_menu", function(module) {
				module.init(editor);
				editor.showKeyboardShortcuts();
			});
		}
	}, {
		name : "increaseFontSize",
		bindKey : "Ctrl-=|Ctrl-+",
		exec : function(editor) {
			var size = parseInt(editor.getFontSize(), 10) || 12;
			editor.setFontSize(size + 1);
		}
	}, {
		name : "decreaseFontSize",
		bindKey : "Ctrl+-|Ctrl-_",
		exec : function(editor) {
			var size = parseInt(editor.getFontSize(), 10) || 12;
			editor.setFontSize(Math.max(size - 1 || 1));
		}
	}, {
		name : "resetFontSize",
		bindKey : "Ctrl+0|Ctrl-Numpad0",
		exec : function(editor) {
			editor.setFontSize(12);
		}
	} ]);

	env.editor.commands.addCommands(whitespace.commands);

	cmdLine.commands.bindKeys({
		"Shift-Return|Ctrl-Return|Alt-Return" : function(cmdLine) {
			cmdLine.insert("\n");
		},
		"Esc|Shift-Esc" : function(cmdLine) {
			cmdLine.editor.focus();
		},
		"Return" : function(cmdLine) {
			var command = cmdLine.getValue().split(/\s+/);
			var editor = cmdLine.editor;
			editor.commands.exec(command[0], editor, command[1]);
			editor.focus();
		}
	});

	cmdLine.commands.removeCommands([ "find", "gotoline", "findall", "replace",
			"replaceall" ]);

	var commands = env.editor.commands;
	commands.addCommand({
		name : "save",
		bindKey : {
			win : "Ctrl-S",
			mac : "Command-S"
		},
		exec : function(arg) {
			var session = env.editor.session;
			var name = session.name.match(/[^\/]+$/);
			localStorage.setItem("saved_file:" + name, session.getValue());
			env.editor.cmdLine.setValue("saved " + name);
		}
	});

	commands.addCommand({
		name : "load",
		bindKey : {
			win : "Ctrl-O",
			mac : "Command-O"
		},
		exec : function(arg) {
			var session = env.editor.session;
			var name = session.name.match(/[^\/]+$/);
			var value = localStorage.getItem("saved_file:" + name);
			if (typeof value == "string") {
				session.setValue(value);
				env.editor.cmdLine.setValue("loaded " + name);
			} else {
				env.editor.cmdLine.setValue("no previuos value saved for "
						+ name);
			}
		}
	});

	var keybindings = {
		ace : null, // Null = use "default" keymapping
		vim : require("ace/keyboard/vim").handler,
		emacs : "ace/keyboard/emacs",
		// This is a way to define simple keyboard remappings
		custom : new HashHandler({
			"gotoright" : "Tab",
			"indent" : "]",
			"outdent" : "[",
			"gotolinestart" : "^",
			"gotolineend" : "$"
		})
	};

	/*********** manage layout ***************************/
	var consoleHeight = 20;
	function onResize() {
		var left = env.split.$container.offsetLeft;
		var width = document.documentElement.clientWidth - left;
		container.style.width = width + "px";
		container.style.height = document.documentElement.clientHeight
				- consoleHeight + "px";
		env.split.resize();

		consoleEl.style.width = width + "px";
		cmdLine.resize();
	}

	window.onresize = onResize;
	onResize();

	/*********** options panel ***************************/
	var docEl = document.getElementById("doc");
	var modeEl = document.getElementById("mode");
	var wrapModeEl = document.getElementById("soft_wrap");
	var themeEl = document.getElementById("theme");
	var foldingEl = document.getElementById("folding");
	var selectStyleEl = document.getElementById("select_style");
	var highlightActiveEl = document.getElementById("highlight_active");
	var showHiddenEl = document.getElementById("show_hidden");
	var showGutterEl = document.getElementById("show_gutter");
	var showPrintMarginEl = document.getElementById("show_print_margin");
	var highlightSelectedWordE = document
			.getElementById("highlight_selected_word");
	var showHScrollEl = document.getElementById("show_hscroll");
	var showVScrollEl = document.getElementById("show_vscroll");
	var animateScrollEl = document.getElementById("animate_scroll");
	var softTabEl = document.getElementById("soft_tab");
	var behavioursEl = document.getElementById("enable_behaviours");

	fillDropdown(docEl, doclist.all);

	fillDropdown(modeEl, modelist.modes);
	var modesByName = modelist.modesByName;
	bindDropdown("mode", function(value) {
		env.editor.session.setMode(modesByName[value].mode
				|| modesByName.text.mode);
		env.editor.session.modeName = value;
	});

	doclist.history = doclist.docs.map(function(doc) {
		return doc.name;
	});
	doclist.history.index = 0;
	doclist.cycleOpen = function(editor, dir) {
		var h = this.history;
		h.index += dir;
		if (h.index >= h.length)
			h.index = 0;
		else if (h.index <= 0)
			h.index = h.length - 1;
		var s = h[h.index];
		docEl.value = s;
		docEl.onchange();
	};
	doclist.addToHistory = function(name) {
		var h = this.history;
		var i = h.indexOf(name);
		if (i != h.index) {
			if (i != -1)
				h.splice(i, 1);
			h.index = h.push(name);
		}
	};

	bindDropdown("doc", function(name) {
		doclist.loadDoc(name, function(session) {
			if (!session)
				return;
			doclist.addToHistory(session.name);
			session = env.split.setSession(session);
			whitespace.detectIndentation(session);
			updateUIEditorOptions();
			env.editor.focus();
		});
	});

	function updateUIEditorOptions() {
		var editor = env.editor;
		var session = editor.session;

		session.setFoldStyle(foldingEl.value);

		saveOption(docEl, session.name);
		saveOption(modeEl, session.modeName || "text");
		saveOption(wrapModeEl, session.getUseWrapMode() ? session
				.getWrapLimitRange().min
				|| "free" : "off");

		saveOption(selectStyleEl, editor.getSelectionStyle() == "line");
		saveOption(themeEl, editor.getTheme());
		saveOption(highlightActiveEl, editor.getHighlightActiveLine());
		saveOption(showHiddenEl, editor.getShowInvisibles());
		saveOption(showGutterEl, editor.renderer.getShowGutter());
		saveOption(showPrintMarginEl, editor.renderer.getShowPrintMargin());
		saveOption(highlightSelectedWordE, editor.getHighlightSelectedWord());
		saveOption(showHScrollEl, editor.renderer.getHScrollBarAlwaysVisible());
		saveOption(animateScrollEl, editor.getAnimatedScroll());
		saveOption(softTabEl, session.getUseSoftTabs());
		saveOption(behavioursEl, editor.getBehavioursEnabled());
	}

	themelist.themes.forEach(function(x) {
		x.value = x.theme
	});
	fillDropdown(themeEl, {
		Bright : themelist.themes.filter(function(x) {
			return !x.isDark
		}),
		Dark : themelist.themes.filter(function(x) {
			return x.isDark
		})
	});

	event.addListener(themeEl, "mouseover", function(e) {
		themeEl.desiredValue = e.target.value;
		if (!themeEl.$timer)
			themeEl.$timer = setTimeout(themeEl.updateTheme);
	});

	event.addListener(themeEl, "mouseout", function(e) {
		themeEl.desiredValue = null;
		if (!themeEl.$timer)
			themeEl.$timer = setTimeout(themeEl.updateTheme, 20);
	});

	themeEl.updateTheme = function() {
		env.split.setTheme((themeEl.desiredValue || themeEl.selectedValue));
		themeEl.$timer = null;
	};

	bindDropdown("theme", function(value) {
		if (!value)
			return;
		env.editor.setTheme(value);
		themeEl.selectedValue = value;
	});

	bindDropdown("keybinding", function(value) {
		env.editor.setKeyboardHandler(keybindings[value]);
	});

	bindDropdown("fontsize", function(value) {
		env.split.setFontSize(value);
	});

	bindDropdown("folding", function(value) {
		env.editor.session.setFoldStyle(value);
		env.editor.setShowFoldWidgets(value !== "manual");
	});

	bindDropdown("soft_wrap", function(value) {
		env.editor.setOption("wrap", value);
	});

	bindCheckbox("select_style", function(checked) {
		env.editor.setOption("selectionStyle", checked ? "line" : "text");
	});

	bindCheckbox("highlight_active", function(checked) {
		env.editor.setHighlightActiveLine(checked);
	});

	bindCheckbox("show_hidden", function(checked) {
		env.editor.setShowInvisibles(checked);
	});

	bindCheckbox("display_indent_guides", function(checked) {
		env.editor.setDisplayIndentGuides(checked);
	});

	bindCheckbox("show_gutter", function(checked) {
		env.editor.renderer.setShowGutter(checked);
	});

	bindCheckbox("show_print_margin", function(checked) {
		env.editor.renderer.setShowPrintMargin(checked);
	});

	bindCheckbox("highlight_selected_word", function(checked) {
		env.editor.setHighlightSelectedWord(checked);
	});

	bindCheckbox("show_hscroll", function(checked) {
		env.editor.setOption("hScrollBarAlwaysVisible", checked);
	});

	bindCheckbox("show_vscroll", function(checked) {
		env.editor.setOption("vScrollBarAlwaysVisible", checked);
	});

	bindCheckbox("animate_scroll", function(checked) {
		env.editor.setAnimatedScroll(checked);
	});

	bindCheckbox("soft_tab", function(checked) {
		env.editor.session.setUseSoftTabs(checked);
	});

	bindCheckbox("enable_behaviours", function(checked) {
		env.editor.setBehavioursEnabled(checked);
	});

	bindCheckbox("fade_fold_widgets", function(checked) {
		env.editor.setFadeFoldWidgets(checked);
	});
	bindCheckbox("read_only", function(checked) {
		env.editor.setReadOnly(checked);
	});
	bindCheckbox("scrollPastEnd", function(checked) {
		env.editor.setOption("scrollPastEnd", checked);
	});

	bindDropdown("split", function(value) {
		var sp = env.split;
		if (value == "none") {
			sp.setSplits(1);
		} else {
			var newEditor = (sp.getSplits() == 1);
			sp.setOrientation(value == "below" ? sp.BELOW : sp.BESIDE);
			sp.setSplits(2);

			if (newEditor) {
				var session = sp.getEditor(0).session;
				var newSession = sp.setSession(session, 1);
				newSession.name = session.name;
			}
		}
	});

	bindCheckbox("elastic_tabstops", function(checked) {
		env.editor.setOption("useElasticTabstops", checked);
	});

	var iSearchCheckbox = bindCheckbox("isearch", function(checked) {
		env.editor.setOption("useIncrementalSearch", checked);
	});

	env.editor.addEventListener('incrementalSearchSettingChanged', function(
			event) {
		iSearchCheckbox.checked = event.isEnabled;
	});

	function synchroniseScrolling() {
		var s1 = env.split.$editors[0].session;
		var s2 = env.split.$editors[1].session;
		s1.on('changeScrollTop', function(pos) {
			s2.setScrollTop(pos)
		});
		s2.on('changeScrollTop', function(pos) {
			s1.setScrollTop(pos)
		});
		s1.on('changeScrollLeft', function(pos) {
			s2.setScrollLeft(pos)
		});
		s2.on('changeScrollLeft', function(pos) {
			s1.setScrollLeft(pos)
		});
	}

	bindCheckbox("highlight_token", function(checked) {
		var editor = env.editor;
		if (editor.tokenTooltip && !checked) {
			editor.tokenTooltip.destroy();
			delete editor.tokenTooltip;
		} else if (checked) {
			editor.tokenTooltip = new TokenTooltip(editor);
		}
	});

	var StatusBar = require("ace/ext/statusbar").StatusBar;
	new StatusBar(env.editor, cmdLine.container);

	var Emmet = require("ace/ext/emmet");
	net.loadScript("https://cloud9ide.github.io/emmet-core/emmet.js",
			function() {
				Emmet.setCore(window.emmet);
				env.editor.setOption("enableEmmet", true);
			});

	// require("ace/placeholder").PlaceHolder;

	var snippetManager = require("ace/snippets").snippetManager;

	env.editSnippets = function() {
		var sp = env.split;
		if (sp.getSplits() == 2) {
			sp.setSplits(1);
			return;
		}
		sp.setSplits(1);
		sp.setSplits(2);
		sp.setOrientation(sp.BESIDE);
		var editor = sp.$editors[1];
		var id = sp.$editors[0].session.$mode.$id || "";
		var m = snippetManager.files[id];
		if (!doclist["snippets/" + id]) {
			var text = m.snippetText;
			var s = doclist.initDoc(text, "", {});
			s.setMode("ace/mode/snippets");
			doclist["snippets/" + id] = s;
		}
		editor.on("blur", function() {
			m.snippetText = editor.getValue();
			snippetManager.unregister(m.snippets);
			m.snippets = snippetManager
					.parseSnippetFile(m.snippetText, m.scope);
			snippetManager.register(m.snippets);
		});
		sp.$editors[0].once("changeMode", function() {
			sp.setSplits(1);
		});
		editor.setSession(doclist["snippets/" + id], 1);
		editor.focus();
	};

	require("ace/ext/language_tools");
	env.editor.setOptions({
		enableBasicAutocompletion : true,
		enableLiveAutocompletion : false,
		enableSnippets : true
	});

	var beautify = require("ace/ext/beautify");
	env.editor.commands.addCommands(beautify.commands);

});
